#! /usr/bin/env python3

import argparse
import shutil
import subprocess
import sys
import os

from oclintscripts import environment
from oclintscripts import path
from oclintscripts import process
from oclintscripts import version

bundle_root_dir = path.build.bundle_dir
bundle_bin_dir = os.path.join(bundle_root_dir, 'bin')
bundle_lib_dir = os.path.join(bundle_root_dir, 'lib')
bundle_lib_oclint_dir = os.path.join(bundle_lib_dir, 'oclint')
bundle_include_dir = os.path.join(bundle_root_dir, 'include')

arg_parser = argparse.ArgumentParser()
arg_parser.add_argument('-archive', '--archive', action='store_true')
arg_parser.add_argument('-docgen', '--docgen', action='store_true')
arg_parser.add_argument('-release', '--release', action='store_true')
arg_parser.add_argument('-llvm-root', '--llvm-root', default=path.build.clang_install_dir)
args = arg_parser.parse_args()

def clean_module(module_name):
    build_path = path.oclint_module_build_dir(module_name)
    path.rm_f(build_path)

def setup_release_dir():
    clean_module('release')
    path.mkdir_p(bundle_root_dir)
    path.mkdir_p(bundle_lib_dir)
    path.mkdir_p(bundle_lib_oclint_dir)

def install_binary(is_release):
    driver_bin_dir = os.path.join(path.build.driver_build_dir, 'bin')
    path.cp_r(driver_bin_dir, bundle_bin_dir)
    if environment.is_unix():
        path.cd(bundle_bin_dir)
        process.call('ln -s oclint-' + version.oclint_version() + ' oclint')
        if not is_release:
            process.call('ln -s oclint-' + version.oclint_version() + ' oclint-' + version.oclint_dev_version())
    if environment.is_mingw32():
        ruleset_dll_name = 'libOCLintRuleSet.dll'
        ruleset_dll_path = os.path.join(path.build.core_build_dir, 'lib', ruleset_dll_name)
        ruleset_dll_dst_path = os.path.join(bundle_bin_dir, ruleset_dll_name)
        path.cp(ruleset_dll_path, ruleset_dll_dst_path)

        abstractrule_dll_name = 'libOCLintAbstractRule.dll'
        abstractrule_dll_path = os.path.join(path.build.rules_build_dir, 'bin', abstractrule_dll_name)
        abstractrule_dll_dst_path = os.path.join(bundle_bin_dir, abstractrule_dll_name)
        path.cp(abstractrule_dll_path, abstractrule_dll_dst_path)

        metric_dll_name = 'libOCLintMetric.dll'
        metric_dll_path = os.path.join(path.build.metrics_build_dir, 'bin', metric_dll_name)
        metric_dll_dst_path = os.path.join(bundle_bin_dir, metric_dll_name)
        path.cp(metric_dll_path, metric_dll_dst_path)

        path.mv(os.path.join(bundle_bin_dir, 'oclint-' + version.oclint_version() + '.exe'), os.path.join(bundle_bin_dir, 'oclint.exe'))

def install_rules():
    rules_src_path = os.path.join(path.build.rules_build_dir, 'rules.dl')
    rules_dst_path = os.path.join(bundle_lib_oclint_dir, 'rules')
    path.cp_r(rules_src_path, rules_dst_path)

def install_reporters():
    reporters_src_path = os.path.join(path.build.reporters_build_dir, 'reporters.dl')
    reporters_dst_path = os.path.join(bundle_lib_oclint_dir, 'reporters')
    path.cp_r(reporters_src_path, reporters_dst_path)

def install_clang_headers(llvm_root):
    clang_headers_src_path = os.path.join(llvm_root, 'lib', 'clang')
    clang_headers_dst_path = os.path.join(bundle_lib_dir, 'clang')
    path.cp_r(clang_headers_src_path, clang_headers_dst_path)

def install_json_compilation_database():
    json_cd_name = 'oclint-json-compilation-database'
    json_cd_src_path = os.path.join(path.source.json_compilation_database_dir, json_cd_name)
    json_cd_dst_path = os.path.join(bundle_bin_dir, json_cd_name)
    if os.path.isfile(json_cd_src_path):
        path.cp(json_cd_src_path, json_cd_dst_path)

def install_xcodebuild():
    if environment.is_darwin():
        xcodebuild_name = 'oclint-xcodebuild'
        xcodebuild_src_path = os.path.join(path.source.xcodebuild_dir, xcodebuild_name)
        xcodebuild_dst_path = os.path.join(bundle_bin_dir, xcodebuild_name)
        if os.path.isfile(xcodebuild_src_path):
            path.cp(xcodebuild_src_path, xcodebuild_dst_path)

def install_cpp_headers():
    if environment.is_darwin() and not environment.is_aarch64_or_arm64():
        install_dir = (path.build.clang_install_dir if not args.llvm_root else args.llvm_root)
        clang_cpp_headers_dir = os.path.join(install_dir, 'include', 'c++')
        oclint_cpp_headers_dir = os.path.join(bundle_include_dir, 'c++')
        path.mkdir_p(bundle_include_dir)
        path.cp_r(clang_cpp_headers_dir, oclint_cpp_headers_dir)

def install_licenses():
    license_path = os.path.join(bundle_root_dir, 'LICENSE')
    oclint_license_path = os.path.join(path.root_dir, 'LICENSE')
    with open(license_path, 'w') as license_file:
        with open(oclint_license_path, 'r') as oclint_license_file:
            # Write OCLint License
            license_file.write(oclint_license_file.read())
            # Find LLVM License and append it to the License
            for d in (args.llvm_root, path.source.clang_dir):
                llvm_license_path = os.path.join(d, 'LICENSE.TXT')
                if os.path.exists(llvm_license_path):
                    break
            if os.path.exists(llvm_license_path):
                license_file.write('\n')
                with open(llvm_license_path, 'r') as llvm_license_file:
                    for _it in range(44):
                        license_file.write(llvm_license_file.readline())

def archive(is_release):
    full_version = version.oclint_version()
    if not is_release:
        full_version = version.oclint_dev_version()
    distribution_name = full_version + '-' + environment.dist_env()
    archive_dir = path.oclint_module_build_dir(full_version)
    path.mv(bundle_root_dir, archive_dir)
    archive_file_name = path.oclint_module_build_dir(distribution_name)
    if environment.is_mingw32():
        shutil.make_archive(archive_file_name, 'zip', path.build_root, 'oclint-' + full_version)
    if environment.is_darwin() or environment.is_linux():
        shutil.make_archive(archive_file_name, 'gztar', path.build_root, 'oclint-' + full_version)

setup_release_dir()
install_licenses()
install_binary(args.release)
install_rules()
if not args.docgen:
    install_reporters()
    install_clang_headers(args.llvm_root)
    install_json_compilation_database()
    if environment.is_darwin():
        install_xcodebuild()
        install_cpp_headers()

if args.archive:
    archive(args.release)
